package io.github.openfeign.spring.enhancer;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.NotFoundException;
import lombok.extern.slf4j.Slf4j;

/**
 * Feign 输出流字节码转换器
 *
 * <h3>核心改造：</h3>
 * <ul>
 *   <li>将内存存储模式改造为流式输出模式</li>
 *   <li>实现以下关键修改：
 *     <ol>
 *       <li>将 {@code ByteArrayOutputStream} 类型字段替换为通用 {@code OutputStream}</li>
 *       <li>重构构造函数，支持外部注入输出流实现</li>
 *       <li>移除内存缓冲机制，实现零拷贝数据传输</li>
 *     </ol>
 *   </li>
 * </ul>
 *
 * <h3>字节码修改对比：</h3>
 *
 * <h4>原始实现（内存模式）:</h4>
 * <pre>{@code
 * // 字段定义
 * private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 *
 * // 构造方法
 * public Output(Charset charset) {
 *     this.charset = charset;
 * }
 * }</pre>
 *
 * <h4>优化后实现（流式模式）:</h4>
 * <pre>{@code
 * // 字段类型替换
 * private OutputStream outputStream;
 *
 * // 增强构造方法
 * public Output(Charset charset, OutputStream stream) {
 *     this.charset = charset;
 *     this.outputStream = stream; // 支持自定义输出流
 * }
 * }</pre>
 *
 *
 * @see java.io.ByteArrayOutputStream 被替换的原始类型
 * @see java.io.OutputStream 替换后的通用接口
 */
@Slf4j
public class OutputBytecodeTransformer extends AbstractBytecodeTransformer {

    public OutputBytecodeTransformer(final ClassPool classPool, final String classOutPath) {
        super(classPool, classOutPath);
    }

    /**
     * {@inheritDoc}
     */
    public void transformBytecode() throws NotFoundException, CannotCompileException {
        if(log.isDebugEnabled()){
            log.debug("Start transforming feign.form.multipart.Output");
        }
        // 加载目标类（替换实际包名）
        final CtClass ctClass = classPool.get("feign.form.multipart.Output");

        // 删除旧构造函数（根据原参数类型）
        final CtClass charsetClass = classPool.get("java.nio.charset.Charset");
        final CtConstructor oldConstructor = ctClass.getDeclaredConstructor(new CtClass[]{charsetClass});
        ctClass.removeConstructor(oldConstructor);

        // 修改字段类型  ByteArrayOutputStream outputStream 为 java.io.OutputStream
        final CtClass outputStreamClass = classPool.get("java.io.OutputStream");
        ctClass.getDeclaredField("outputStream").setType(outputStreamClass);

        // 创建新构造函数（参数：Charset + OutputStream）
        final CtConstructor newConstructor = new CtConstructor(new CtClass[]{charsetClass, outputStreamClass}, ctClass);
        newConstructor.setBody("{ $0.charset = $1; $0.outputStream = $2; }");
        ctClass.addConstructor(newConstructor);

        // 修改了字段类型，原先的方法找不到新的字段，需要修改
        final CtClass bytesClass = classPool.get("byte[]");
        final CtMethod writeAllBytesMethod = ctClass.getDeclaredMethod("write", new CtClass[]{bytesClass});
        writeAllBytesMethod.setBody("{ $0.outputStream.write($1); return $0; }");

        // 修改了字段类型，原先的方法找不到新的字段，需要修改
        final CtMethod writeBytesMethod = ctClass.getDeclaredMethod("write", new CtClass[]{bytesClass, CtClass.intType, CtClass.intType});
        writeBytesMethod.setBody("{ $0.outputStream.write($1,$2,$3); return $0; }");

        // output 不再支持toBytes
        final CtMethod toBytesMethod = ctClass.getDeclaredMethod("toByteArray");
        toBytesMethod.setBody("{ throw new java.lang.UnsupportedOperationException(); }");

        redefineClassInMemory(ctClass);
        if(log.isDebugEnabled()){
            log.debug("Successfully transforming feign.form.multipart.Output");
        }
    }

}
